/*
* Copyright (c) 2008, Russell Garrett
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.moneydance.modules.features.portfolioallocation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.moneydance.apps.md.model.*;

public class ClassifiedSecurity implements Comparable {
	
	private static Map<Integer, ClassifiedSecurity> securities = new HashMap<Integer, ClassifiedSecurity>();
	private static Map<String, String> classifications;
	private static List<String> classes = new ArrayList<String>();
	
	public static ArrayList<ClassifiedSecurity> getAll(RootAccount rootAccount) {
		  loadClassifications();
		  CurrencyTable currencies = rootAccount.getCurrencyTable();
		  for (CurrencyType c : currencies.getAllCurrencies()) {
			  if (c.getCurrencyType() == CurrencyType.CURRTYPE_SECURITY && 
					  !securities.containsKey(c.getID())) {
				  String cl = classifications.get(Integer.toString(c.getID()));
				  securities.put(c.getID(), new ClassifiedSecurity(c, (cl!=null)?cl:"[unclassified]"));
			  }
		  }
		  return new ArrayList<ClassifiedSecurity>(securities.values());
	}
	
	private static void loadClassifications() {
		if (classifications == null)
			classifications = 
				Settings.instance().loadMapStringString("classifications", new HashMap<String, String>());
	}
	
	private static void saveClassifications() {
		Map<String, String> newClassifications = new HashMap<String, String>();
		for (Map.Entry<Integer, ClassifiedSecurity> e : securities.entrySet()) {
			newClassifications.put(e.getKey().toString(), e.getValue().getAssetClass());
		}
		classifications = newClassifications;
		Settings.instance().saveMapStringString("classifications", newClassifications);
	}
	
	public static ClassifiedSecurity getByID(RootAccount rootAccount, int id) {
		getAll(rootAccount);
		return securities.get(id);
	}
	
	public static List<String> getClasses() {
		return classes;
	}
	
	public static void reset() {
		securities = new HashMap<Integer, ClassifiedSecurity>();
		classifications = null;
		classes = new ArrayList<String>();
	}
	
	public static Map<String, Long> calculateAllocation(RootAccount rootAccount) {
		Map<String, Long> allocation = new HashMap<String, Long>();
		List<Account> accounts = new ArrayList<Account>();
	    int sz = rootAccount.getSubAccountCount();
	    for (int i=0; i<sz; i++) {
	      Account acct = rootAccount.getSubAccount(i);
	      accounts.add(acct);
	    }
	    
	    while (accounts.size() > 0) {
	    	Account a = accounts.get(0);
	    	if (a instanceof SecurityAccount) { // Money invested in securities.
	    		ClassifiedSecurity cs = ClassifiedSecurity.getByID(rootAccount, a.getCurrencyType().getID());

	    		if (cs == null) {
	    			accounts.remove(a);
	    			System.out.println("Currency not found!");
	    			continue;
	    		}
	    		String assetClass = cs.getAssetClass();
	    		long value = CurrencyTable.convertValue(a.getBalance(), 
	    				a.getCurrencyType(), 
	    				rootAccount.getCurrencyTable().getBaseType());
	    		Long amount;
	    		if ((amount = allocation.get(assetClass)) != null){
	    			allocation.put(assetClass, amount + value);
	    		} else {
	    			allocation.put(assetClass, value);
	    		}
	    	} else if ((sz = a.getSubAccountCount()) > 0) {
	    		for (int i=0; i<sz; i++){
	    			accounts.add(a.getSubAccount(i));
	    		}
	    	}
	    	
	    	if (a instanceof InvestmentAccount) { // Get uninvested cash balance of investment accounts.
	    		String assetClass = "Cash";
	    		Long value = a.getBalance();
	    		Long amount;
	    		if ((amount = allocation.get(assetClass)) != null){
	    			allocation.put(assetClass, amount + value);
	    		} else {
	    			allocation.put(assetClass, value);
	    		}
	    	}
	    	accounts.remove(a);
	    }
	    return allocation;
	}
	
	private CurrencyType currencyType;
	private String assetClass;
	
	public ClassifiedSecurity(CurrencyType c, String assetClass){
		this.currencyType = c;
		this.assetClass = assetClass;
		if (!classes.contains(assetClass)){
			classes.add(assetClass);
		}
	}
	
	public CurrencyType getCurrencyType() {
		return currencyType;
	}
	
	public String getAssetClass(){
		return assetClass;
	}
	
	public void setAssetClass(String assetClass){
		this.assetClass = assetClass;
		if (!classes.contains(assetClass)){
			classes.add(assetClass);
		}
		saveClassifications();
	}

	public String toString() {
		return currencyType.getName();
	}

	public int compareTo(Object b) {
		return currencyType.getName().compareTo(
				((ClassifiedSecurity)b).getCurrencyType().getName());
	}
}
